package socket.protocol;


import io.reactivex.schedulers.Schedulers;
import socket.PLCFactory;
import socket.modle.ConnectedCallback;
import socket.modle.PlcInterface;
import socket.util.ByteUtil;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.SocketException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

/**
 * Created by bpb on 2021-01-17.
 * 说明：TCP 版本
 */
public class ProtocolServerType1 extends IProtocolServer implements ConnectedCallback {

    //AA cmd rand len xx xx
    Logger log = Logger.getLogger("ProtocolServerType1");

    private PlcInterface plcInterface;


    private Object mSendLock = new Object();

    private Timer timer = new Timer();

    private ConcurrentHashMap<String, Integer> heartMaps = new ConcurrentHashMap<>();

    public ProtocolServerType1(OnNeedCallback callback) {
        super(callback);
        timer.schedule(new CheckHeart(), new Date(), 10000);
    }


    @Override
    public void onConnectedStatusChange(String key, boolean connected, Exception e, String msg) {
        System.out.println("连接状态 key" + key + " connected:" + connected + "，描述: " + msg);
        onNeedCallback.onConnectedStatusChanged(key, connected, e, msg);
    }




    @Override
    public byte[] onReceive(final String key, DataInputStream dataInputStream) {
        // synchronized (ProtocolServerType1.class) {
        try {
            byte[] start = new byte[2];
            byte[] standard = new byte[13];
            int count = dataInputStream.available();
            if (count == 0) {
                return null;
            }
            int length = dataInputStream.read(start);
            if (length == -1) {
                return null;
            }
            if (length != 2) {
                System.out.println(key+" | "+System.currentTimeMillis()+"[TIME] 标准头信息长度错误:" + length+" KEy:"+key);
                return null;
            }

            // 检查头信息
            if (start[0] != headerA || start[1] != headerB) {
                System.out.println(key+" | "+System.currentTimeMillis()+"[TIME] 头信息错误：" + headerA + "|| " + headerB + " 原始数据： "
                        + ByteUtil.byteToHexString(start[0])+" "+ByteUtil.byteToHexString(start[1])+" KEy:"+key);
                return null;
            }

            length = dataInputStream.read(standard);
            if (length != 13) {
                System.out.println(key+" | "+System.currentTimeMillis()+"[TIME]标准信息长度错误:" + length);
                return null;
            }
//                StringBuilder logBuilder = new StringBuilder();
//                logBuilder.append("接收 <<< ");
//                logBuilder.append(headerA);
//                logBuilder.append(" "+headerB);
//                for (int i = 0; i < standard.length; i++) {
//                    logBuilder.append(" " + ByteUtil.byteToHexString(standard[i]));
//                }

            int action = ByteUtil.byteToInt(standard[0]);
            long rTime = ByteUtil.byteArrayToLong(new byte[]{standard[1], standard[2], standard[3], standard[4],standard[5],
                    standard[6],standard[7],standard[8]});
            int byteCount = ByteUtil.getInt(new byte[]{standard[9], standard[10], standard[11], standard[12]});
            byte[] content = getDataAndCheck(dataInputStream, byteCount, null);
         //   System.out.println(key+" | "+System.currentTimeMillis()+"[TIME] "+logBuilder.toString());

            if (action != 0) {
//                Schedulers.computation().scheduleDirect(new Runnable() {
//                    @Override
//                    public void run() {
//                        try {
                            onNeedCallback.checkAction(key, action, rTime, content);
//                        } catch (Exception e) {
//                            e.printStackTrace();
//                        }
//                    }
//                });
            } else {
                //TODO 心跳的
                heartMaps.put(key, 1);
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
        //  }
    }


    /**
     * 读取内容字节并校验长度是否正确
     */
    private byte[] getDataAndCheck(DataInputStream inputStream, int dataLength, StringBuilder logBuilder) throws
            IOException {
        if (dataLength == 0) {
            return null;
        }
        byte[] data = new byte[dataLength];
        int length = inputStream.read(data);

        if (length != dataLength) {
            log.info("内容长度错误:" + length + " != " + dataLength);
            return null;
        }
        if (logBuilder != null) {
            for (int i = 0; i < data.length; i++) {
                logBuilder.append(" " + ByteUtil.byteToHexString(data[i]));
            }
        }
        return data;
    }


    /**
     * 创建socket服务器并等待客户端连接
     *
     * @param retryCount 创建失败时重试的次数
     * @param params     创建通讯方式的参数
     */
    public void connect(int retryCount, Object... params) {
        synchronized (mSendLock) {
            plcInterface = PLCFactory.getPlcUtil(PLCFactory.SOCKET_SERVER, "SERVER").init(this, retryCount);
        }
        plcInterface.connect(params);
    }

    public long getSendTime(){
        return System.currentTimeMillis();
    }

    public void sendData(String target, int cmd, byte[] bytes) {
        try {
            long rTime = getSendTime();
            byte[] tBytes = ByteUtil.toByteArray(rTime);
            int len = bytes == null ? 0 : bytes.length;
            byte[] datas = new byte[7 + len + 8];
            datas[0] = headerA;
            datas[1] = headerB;
            datas[2] = ByteUtil.intToByte(cmd);
            System.arraycopy(tBytes, 0, datas, 3, 8);
            datas[11] = (byte) ((len & 0xff000000) >> 24);
            datas[12] = (byte) ((len & 0xff0000) >> 16);
            datas[13] = (byte) ((len & 0xff00) >> 8);
            datas[14] = (byte) (len & 0xff);
            if (bytes != null) {
                System.arraycopy(bytes, 0, datas, 15, len);
            }
            sendData(target, datas);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void sendData(String key, byte[] datas) {
        if (plcInterface != null) {
            plcInterface.sendData(key, datas);
        }
    }


    @Override
    public void disConnect() {

    }


    /**
     * 退出程序
     */
    public void destroy(String who) {
        if (plcInterface != null) {
            plcInterface.destroy(who);
        }
    }


    private class CheckHeart extends TimerTask {

        @Override
        public void run() {
            ArrayList<String> whoDeads = new ArrayList<>(0);
            Iterator<Map.Entry<String, Integer>> iterator = heartMaps.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Integer> entry = iterator.next();
                if (entry.getValue() == 0) {
                    whoDeads.add(entry.getKey());
                }
                heartMaps.put(entry.getKey(), 0);
            }
            for (String deadKey : whoDeads) {
                heartMaps.remove(deadKey);
                //TODO 清空断开连接
                try {
                    System.out.println(System.currentTimeMillis()+"[TIME] 心跳异常断开 "+deadKey);
                    destroy(deadKey);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
